﻿using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

namespace Learning
{
    [Serializable]
    public class CameraBlurSetting
    {
        public Material material;
    }

    public class CameraBlurFeature : ScriptableRendererFeature
    {
        private static bool _needCapture = false;
        private static List<Action<RenderTexture>> _callbackList = new List<Action<RenderTexture>>();
        
        /// <summary>
        /// 开始对摄像机进行截屏和模糊
        /// </summary>
        /// <param name="onComplete">完成回调</param>
        public static void CameraCapture(Action<RenderTexture> onComplete)
        {
            _needCapture = true;
            _callbackList.Add(onComplete);
        }
        
        // 作为内部类，可以方便调用 CameraBlurFeature 的私有静态字段
        public class CameraBlurPass : ScriptableRenderPass
        {
            private CameraBlurSetting _settings;

            private Vector2[] _sizeArr = { Vector2.zero, Vector2.zero, Vector2.zero };
            
            private RTHandle _tempRT1;
            private RTHandle _tempRT2;
            
            public CameraBlurPass(CameraBlurSetting settings)
            {
                renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
                _settings = settings;
            }

            public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
            {
                // 用于 SceneView 的场景摄像机略过
                if (renderingData.cameraData.isSceneViewCamera) return;
                // 不需要截屏时返回
                if (!_needCapture) return;
                
                var desc = renderingData.cameraData.cameraTargetDescriptor;
                desc.depthBufferBits = 0;
                desc.width /= 2;
                desc.height /= 2;
                
                _sizeArr[0].Set(desc.width, desc.height);
                _sizeArr[1].Set(desc.width / 2, desc.height / 2);
                _sizeArr[2].Set(desc.width / 4, desc.height / 4);

                RenderingUtils.ReAllocateIfNeeded(ref _tempRT1, desc, FilterMode.Point, TextureWrapMode.Clamp, name: "_tempRT1");
                RenderingUtils.ReAllocateIfNeeded(ref _tempRT2, desc, FilterMode.Point, TextureWrapMode.Clamp, name: "_tempRT2");
            }

            public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
            {
                // 用于 SceneView 的场景摄像机略过
                if (renderingData.cameraData.isSceneViewCamera) return;
                // 不需要截屏时返回
                if (!_needCapture) return;

                var cmd = CommandBufferPool.Get("CameraBlur");
                var material = _settings.material;
                Blitter.BlitCameraTexture(cmd, renderingData.cameraData.renderer.cameraColorTargetHandle, _tempRT1);
                for (var i = 0; i < _sizeArr.Length; i++)
                {
                    cmd.SetGlobalVector("blur_offsets", new Vector4(2.0f / _sizeArr[i].x, 0, 0, 0));
                    cmd.Blit(_tempRT1.nameID, _tempRT2.nameID, material);
                    cmd.SetGlobalVector("blur_offsets", new Vector4(0, 2.0f / _sizeArr[i].y, 0, 0));
                    cmd.Blit(_tempRT2.nameID, _tempRT1.nameID, material);
                }
                
                RenderTexture rt = RenderTexture.GetTemporary(_tempRT1.rt.width, _tempRT1.rt.height, 0);
                rt.name = "CameraBlur";
                Blit(cmd, _tempRT1, rt);
                foreach (var callback in _callbackList)
                {
                    callback?.Invoke(rt);
                }
                _callbackList.Clear();
                _needCapture = false;
                
                // 注意：执行和释放 cmd 必须放在拷贝 _tempRT1 之后
                context.ExecuteCommandBuffer(cmd);
                CommandBufferPool.Release(cmd);
            }

            public override void OnCameraCleanup(CommandBuffer cmd)
            {
                RTHandles.Release(_tempRT1);
                RTHandles.Release(_tempRT2);
            }
        }
        
        public CameraBlurSetting settings = new CameraBlurSetting();
        
        private CameraBlurPass _pass;
        
        public override void Create()
        {
            _pass = new CameraBlurPass(settings);
        }

        public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
        {
            renderer.EnqueuePass(_pass);
        }
    }
}
